/*************************************************************************/
/*                                                                       */
/*  Copyright (c) 1994 Stanford University                               */
/*                                                                       */
/*  All rights reserved.                                                 */
/*                                                                       */
/*  Permission is given to use, copy, and modify this software for any   */
/*  non-commercial purpose as long as this copyright notice is not       */
/*  removed.  All other uses, including redistribution in whole or in    */
/*  part, are forbidden without prior written permission.                */
/*                                                                       */
/*  This software is provided with absolutely no warranty and no         */
/*  support.                                                             */
/*                                                                       */
/*************************************************************************/

#include <stdio.h>
#include <math.h>
#include "defs.h"
#include "memory.h"
#include "particle.h"
#include "box.h"

/* How many boxes can fit on one line */
#define BOXES_PER_LINE 4
#define TERMS_PER_LINE 2

box *Grid = NULL;

void ZeroBox(int my_id, box *b);
void PrintExpansionTerms();

void
CreateBoxes (int my_id, int num_boxes) 
{
   int cluster_no;
   int i;
   
   LOCK(G_Memory->mal_lock);
   Local[my_id].B_Heap = (box *) G_MALLOC(num_boxes * sizeof(box));

/* POSSIBLE ENHANCEMENT:  Here is where one might distribute the 
   B_Heap data across physically distributed memories as desired.

   One way to do this is as follows:

   char *starting_address;
   char *ending_address;

   starting_address = (char *) Local[my_id].B_Heap;
   ending_address = (((char *) Local[my_id].B_Heap)
		     + (num_boxes * sizeof(particle *)) - 1);

   Place all addresses x such that (starting_address <= x < ending_address)
   on node my_id

*/

   UNLOCK(G_Memory->mal_lock);
   Local[my_id].Max_B_Heap = num_boxes;
   Local[my_id].Index_B_Heap = 0;

   for (i = 0; i < num_boxes; i++) {
      Local[my_id].B_Heap[i].exp_lock_index = i % (MAX_LOCKS - 1);
      Local[my_id].B_Heap[i].particle_lock_index = i % (MAX_LOCKS - 1);
      Local[my_id].B_Heap[i].id = i + ((double) my_id / ID_LIMIT);
      ZeroBox(my_id, &Local[my_id].B_Heap[i]);
   }
}


void
FreeBoxes (int my_id)
{
   int i;
   box *b_array;
     
   b_array = Local[my_id].B_Heap;
   for (i = 0; i < Local[my_id].Index_B_Heap; i++)
      ZeroBox(my_id, &b_array[i]);
   Local[my_id].Index_B_Heap = 0;
}


void
ZeroBox (int my_id, box *b)
{
   int i;
  
   b->type = CHILDLESS;
   b->num_particles = 0;
   for (i = 0; i < MAX_PARTICLES_PER_BOX; i++)
      b->particles[i] = NULL;
   b->parent = NULL;
   for (i = 0; i < NUM_OFFSPRING; i++) {
      b->children[i] = NULL;
      b->shadow[i] = NULL;
   }
   b->num_children = 0;
   b->construct_synch = 0;
   b->interaction_synch = 0;
   b->cost = 0;
   b->proc = my_id;
   b->subtree_cost = 0;
   b->next = NULL;
   b->prev = NULL;
}


/*
 *  InitBox (int my_id, real x_center, real y_center, real length, int level, box *parent)
 *
 *  Args : the x_center and y_center of the center of the box;
 *         the length of the box;
 *         the level of the box;
 *         the address of b's parent.
 *
 *  Returns : the address of the newly created box.
 *
 *  Side Effects : Initializes num_particles to 0, all other pointers to NULL,
 *    and sets the box ID to a unique number. It also creates the space for 
 *    the two expansion arrays.
 *
 */
box *
InitBox (int my_id, real x_center, real y_center, real length, box *parent)
{
   box *b;
  
   if (Local[my_id].Index_B_Heap == Local[my_id].Max_B_Heap) {
      LockedPrint("ERROR (P%d) : Ran out of boxes\n", my_id);
      exit(-1);
   }
   b = &Local[my_id].B_Heap[Local[my_id].Index_B_Heap++];
   b->x_center = x_center;
   b->y_center = y_center;
   b->length = length;
   b->parent = parent;
   if (parent == NULL)
      b->level = 0;
   else
      b->level = parent->level + 1;
   return b;
}


/*
 *  PrintBox (box *b)
 *
 *  Args : the address of a box, b.
 *
 *  Returns : nothing.
 *
 *  Side Effects : Prints to stdout the information stored for b.
 *
 */
void
PrintBox (box *b)
{
   int i;
  
   LOCK(G_Memory->io_lock);
   fflush(stdout);
   if (b != NULL) {
      printf("Info for B%f :\n", b->id);
      printf("  X center       = %.40g\n", b->x_center);
      printf("  Y center       = %.40g\n", b->y_center);
      printf("  Length         = %.40g\n", b->length);
      printf("  Level          = %d\n", b->level);
      printf("  Type           = %d\n", b->type);
      printf("  Child Num      = %d\n", b->child_num);
      if (b->parent == NULL)
	 printf("  Parent         = NONE\n");
      else
	 printf("  Parent         = B%f\n", b->parent->id);
      printf("  Children's IDs : ");
      if (b->num_children != 0)
	 PrintBoxArrayIds(b->children, b->num_children);
      else
	 printf("NONE\n");
      printf("  Sibling's IDs : ");
      if (b->num_siblings != 0)
	 PrintBoxArrayIds(b->siblings, b->num_siblings);
      else
	 printf("NONE\n");
      printf("  Colleagues' IDs : ");
      PrintBoxArrayIds(b->colleagues, b->num_colleagues);
      printf("  U List IDs : ");
      PrintBoxArrayIds(b->u_list, b->num_u_list);
      printf("  V List IDs : ");
      PrintBoxArrayIds(b->v_list, b->num_v_list);
      printf("  W List IDs : ");
      PrintBoxArrayIds(b->w_list, b->num_w_list);
      printf("  # of Particles = %d\n", b->num_particles);
      printf("  Particles' IDs : ");
      PrintParticleArrayIds(b->particles, b->num_particles);
      printf("  Assigned Process ID : %d\n", b->proc);
      printf("  Cost : %d\n", b->cost);
      printf("\n");
   }
   else
      printf("Box has not been initialized yet.\n\n");
   UNLOCK(G_Memory->io_lock);
}


/*
 *  PrintBoxArrayIds (box_node *b_array[], int array_length)
 *
 *  Args : the address of the box array, b_array;
 *         the length of the array, array_length.
 * 
 *  Returns : nothing.
 *
 *  Side Effects : Prints to stdout just the id numbers for every box in 
 *    b_array.
 *
 */
void
PrintBoxArrayIds (box *b_array[], int array_length)
{
   int i;
   int tab_count;

   tab_count = 0;
   for (i = 0; i < array_length; i++) {
      if (tab_count == 0) {
	 printf("\n");
	 tab_count = BOXES_PER_LINE;
      }
      if (b_array[i] != NULL)
	 printf("\tB%f", b_array[i]->id);
      tab_count -= 1;
   }
   printf("\n");
}


/*
 *  PrintExpansionTerms (real expansion[])
 *
 *  Args : the array of expansion terms, expansion.
 *
 *  Returns : nothing.
 *
 *  Side Effects : Prints to stdout the contents of expansion.
 *
 */
void
PrintExpansionTerms (complex expansion[])
{
   int i;
   int tab_count = 0;
  
   for (i = 0; i < Expansion_Terms; i++) {
      if (tab_count == 0) {
	 printf("\n");
	 tab_count = TERMS_PER_LINE;
      }
      if (expansion[i].i >= (real) 0.0)
	 printf("\ta%d = %.3e + %.3ei", i, expansion[i].r, expansion[i].i);
      else
	 printf("\ta%d = %.3e - %.3ei", i, expansion[i].r, -expansion[i].i);
      tab_count -= 1;
   }
   printf("\n");
}


void
ListIterate (int my_id, box *b, box **list, int length, list_function function)
{
   int i;

   for (i = 0; i < length; i++) {
      if (list[i] == NULL) {
	 LockedPrint("ERROR (P%d) : NULL list entry\n", my_id);
	 exit(-1);
      }
      (*function)(my_id, list[i], b);
   }
}


/*
 *  AdjacentBoxes (int my_id, box *b1, box *b2)
 *
 *  Args : two potentially adjacent boxes, b1 and b2.
 *
 *  Returns : TRUE, if boxes are adjacent, FALSE if not.
 *
 *  Side Effects : none.
 *
 *  Comments : Two boxes are adjacent if their centers are separated in either
 *     the x or y directions by (1/2 the length of b1) + (1/2 length of b2),
 *     and separated in the other direction by a distance less than or equal
 *     to (1/2 the length of b1) + (1/2 the length of b2).
 *
 *     NOTE : By this definition, parents are NOT adjacent to their children.
 */
int
AdjacentBoxes (int my_id, box *b1, box *b2)
{
   real exact_separation;
   real x_separation;
   real y_separation;
   int ret_val;
      
   exact_separation = (b1->length / (real) 2.0) + (b2->length / (real) 2.0);
   x_separation = (real) fabs((double)(b1->x_center - b2->x_center));
   y_separation = (real) fabs((double)(b1->y_center - b2->y_center));
  
   if ((x_separation == exact_separation) && 
       (y_separation <= exact_separation))
      ret_val = TRUE;
   else
      if ((y_separation == exact_separation) && 
	  (x_separation <= exact_separation))
	 ret_val = TRUE;
      else
	 ret_val = FALSE;
  
   return ret_val;
}


/*  
 *  WellSeparatedBoxes (int my_id, box *b1, box *b2)
 *
 *  Args : Two potentially well separated boxes, b1 and b2.
 *
 *  Returns : TRUE, if the two boxes are well separated, and FALSE if not.
 *
 *  Side Effects : none.
 *
 *  Comments : Well separated means that the two boxes are separated by the
 *     length of the boxes. If one of the boxes is bigger than the other,
 *     the smaller box is given the length of the larger box. This means
 *     that the centers of the two boxes, regardless of their relative size,
 *     must be separated in the x or y direction (or both) by at least
 *     twice the length of the biggest box.
 *
 */
int
WellSeparatedBoxes (int my_id, box *b1, box *b2)
{
   real min_ws_distance;
   real x_separation;
   real y_separation;
   int ret_val;
      
   if (b1->length > b2->length)
      min_ws_distance = b1->length * (real) 2.0;
   else
      min_ws_distance = b2->length * (real) 2.0;

   x_separation = (real) fabs((double)(b1->x_center - b2->x_center));
   y_separation = (real) fabs((double)(b1->y_center - b2->y_center));
    
   if ((x_separation >= min_ws_distance) || (y_separation >= min_ws_distance))
      ret_val = TRUE;
   else
      ret_val = FALSE;

   return ret_val;
}


#undef BOXES_PER_LINE
#undef TERMS_PER_LINE

